#include "graphics/renderer.h"
#include "graphics/graphicsmanager.h"

namespace dw {
	namespace graphics {

Renderer::Renderer()
:	_currentTextureUnit(0)
,	m_indexBuffer(0)
,	_currentProgram(0)
{
	_matrixStack.push(Matrix44f());
	_matrix = &_matrixStack.top();
	_stateStack.push(State());
	_currentState = &_stateStack.top();
}

Renderer::~Renderer()
{

}

void Renderer::setRenderTarget(RenderTarget* target)
{
	GraphicsManager::getInstance().getBackend().setRenderTarget(target);
}

void Renderer::beginRender()
{
	GraphicsManager::getInstance().getBackend().clearRenderTarget();
}

void Renderer::endRender()
{
	GraphicsManager::getInstance().getBackend().present();
}

#define BUFFER_OFFSET(i) ((char*)NULL + (i))

void Renderer::drawIndexedPrimitive(primitive::types primitiveType, dw::uint32 indexStart, dw::uint32 indexCount)
{
	IndexBuffer::indextypes indexType = m_indexBuffer->getIndexType();
	GLsizei indexSize = IndexBuffer::glIndexSize(indexType);

	glDrawElements(primitive::glType(primitiveType), indexCount, IndexBuffer::glType(indexType), BUFFER_OFFSET(indexStart * indexSize));
}

void Renderer::drawIndexedPrimitive(primitive::types primitiveType, dw::uint32 indexStart, dw::uint32 indexCount,
									dw::uint32 indexMin, dw::uint32 indexMax)
{
	IndexBuffer::indextypes indexType = m_indexBuffer->getIndexType();
	GLsizei indexSize = IndexBuffer::glIndexSize(indexType);
	GLenum glPrimitiveType = primitive::glType(primitiveType);
	GLenum glIndexType = IndexBuffer::glType(indexType);

	glDrawRangeElements(glPrimitiveType, indexMin, indexMax, indexCount, glIndexType, BUFFER_OFFSET(indexStart * indexSize));
}

#undef BUFFER_OFFSET

std::ostream& operator<<(std::ostream& os, const Renderer::TextureUnit::State& textureUnitState)
{ 
	uint32 numValues = static_cast< uint32 >(textureUnitState._values.size());

	os.write(reinterpret_cast< const char* >(&numValues), sizeof (uint32));

	for (Renderer::TextureUnit::State::StateConstIterator iter = textureUnitState._values.begin(); iter != textureUnitState._values.end(); ++iter)
	{
		os.write(reinterpret_cast< const char* >(&iter->first), sizeof (Renderer::TextureUnit::State::StateMap::key_type));
		os.write(reinterpret_cast< const char* >(&iter->second), sizeof (Renderer::TextureUnit::State::StateMap::mapped_type));
	}
	return os;
}

std::istream& operator>>(std::istream& is, Renderer::TextureUnit::State& textureUnitState)
{
	uint32 numValues;

	is.read(reinterpret_cast< char* >(&numValues), sizeof (uint32));
	for (unsigned i = 0; i < numValues; ++i)
	{
		std::pair<Renderer::TextureUnit::State::StateMap::key_type, Renderer::TextureUnit::State::StateMap::mapped_type> state;
		is.read(reinterpret_cast< char* >(&state.first), sizeof (Renderer::TextureUnit::State::StateMap::key_type));
		is.read(reinterpret_cast< char* >(&state.second), sizeof (Renderer::TextureUnit::State::StateMap::mapped_type));
		textureUnitState._values.insert(state);
	}
	return is;
}

	} // namespace graphics
} // namespace dw